/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024.
 * Description: set maximum low memory through bootargs
 * with vmalloc parameter
 * Author: heyuqiang
 * Create: 2024-10-08
 */

#include <linux/types.h>
#include <mm/mmu_decl.h>
#include <linux/highmem.h>
/*
 * when early_vmalloc called, the VMALLOC_END haven't initialition,
 * so used IOREMAP_TOP instead of VMALLOC_END.
 */
#define VMALLOC_RESERVE_MAX (__pa(IOREMAP_TOP) - (SZ_64M + VMALLOC_OFFSET))
#define BYTES_TO_MB(b) ((b) >> 20UL)

/*
 * vmalloc=size forces the vmalloc area to be exactly 'size'
 * bytes. This can be used to increase (or decrease) the low
 * memory  area. Thus this can be also used to decrease (or increase)
 * low memory area.
 */
static int __init early_vmalloc(char *arg)
{
	unsigned long vmalloc_reserve = memparse(arg, NULL);

	if (vmalloc_reserve < SZ_16M) {
		vmalloc_reserve = SZ_16M;
		pr_warn("vmalloc area too small, limiting to %luMB\n",
			BYTES_TO_MB(vmalloc_reserve));
	}

	if (vmalloc_reserve > VMALLOC_RESERVE_MAX) {
		vmalloc_reserve = VMALLOC_RESERVE_MAX;
		pr_warn("vmalloc area is too big, limiting to %luMB\n",
			BYTES_TO_MB(vmalloc_reserve));
	}

	/* low memory aligned 16M */
	__max_low_memory = __pa(IOREMAP_TOP) - VMALLOC_OFFSET - vmalloc_reserve;
	__max_low_memory &= ~(SZ_16M - 1);
	return 0;
}
early_param("vmalloc", early_vmalloc);
